﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GestureSaver;
using Microsoft.Kinect;
using System.IO;

namespace Experimenter
{
    class Algorithm2 : IAlgorithm
    {
        int numJoints = Common.SelectedJoints.Length;
        GestureDB trainingDatabase;
        Dictionary<JointType, double> weights;

        public void Train(GestureDB trainDB)
        {
            this.trainingDatabase = trainDB;
            Common.CenterizeAllGestures(trainingDatabase);
            Common.NormalizeAllShoulderLengths(trainingDatabase); 
            // Weight computation begins
            weights = new Dictionary<JointType, double>();
            
            double[,,] D = new double[numJoints, trainingDatabase.TotalClassCount, trainingDatabase.TotalClassCount];
           
            for (int p = 0; p < numJoints; p++)
            {
                for (int i = 0; i < trainingDatabase.TotalClassCount; i++)
                {
                    for (int j = 0; j < trainingDatabase.TotalClassCount; j++)
                    {
                        double mean = 0;
                        for (int k = 0; k < trainingDatabase[i].Count; k++)
                        {
                            for (int l = 0; l < trainingDatabase[j].Count; l++)
                            {
                                Gesture g1 = trainingDatabase[j][l];
                                Gesture g2 = trainingDatabase[i][k];

                                mean += Common.DTW(g1, g2, new JointType[] { Common.SelectedJoints[p] });
                            }
                        }
                        mean /= trainingDatabase[i].Count * trainingDatabase[j].Count;
                        D[p, i, j] = mean;
                    }
                }
                double interClassVariability = 0;
                double intraClassVariability = 0;
                for (int i = 0; i < trainingDatabase.TotalClassCount - 1; i++)
                {
                    for (int j = i + 1; j <= trainingDatabase.TotalClassCount - 1; j++)
                    {
                        interClassVariability += D[p, i, j];
                    }
                }
                interClassVariability /= (trainingDatabase.TotalClassCount * (trainingDatabase.TotalClassCount - 1)) / 2;
                for (int i = 0; i < trainingDatabase.TotalClassCount; i++)
                {
                    intraClassVariability += D[p, i, i];
                }
                intraClassVariability /= trainingDatabase.TotalClassCount;
                double Vp = Math.Max(0, (interClassVariability - intraClassVariability) / (interClassVariability));
                weights.Add(Common.SelectedJoints[p], Vp);
            }

            Common.NormalizeWeights(weights);
            //Weight computation ends
            using (TextWriter writer = File.CreateText(Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + @"\weightsAlg2.txt"))
            {
                for (int j = 0; j < weights.Count; j++)
                {
                    writer.Write(weights.Keys.ElementAt(j) + "  ->  ");
                    writer.WriteLine(weights.Values.ElementAt(j));
                }
            }
        }

        public GestureType Recognize(Gesture g)
        {
            Common.CenterizeGesture(g);
            Common.NormalizeShoulderLength(g);
            
            double minCost = Double.PositiveInfinity;
            GestureType minCostType = GestureType.UnknownGestureType;

            for (int i = 0; i < trainingDatabase.TotalClassCount; i++)
            {
                double[] costs = new double[trainingDatabase[i].Count];

                for (int j = 0; j < trainingDatabase[i].Count; j++)
                {
                    costs[j] = Common.DTW(g, trainingDatabase[i][j], null, weights); // Weights should be passed from here... 
                }

                Array.Sort(costs);
                double medianCost = costs[0];

                if (medianCost < minCost)
                {
                    minCost = medianCost;
                    minCostType = trainingDatabase[i][0].gestureType;
                }
            }

            return minCostType;
        }
    }
}
